package org.nielin.bestwaper.core.filter;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @author rockydai
 * 
 * 1.  IP白名单检查;
 * 2.  IP黑名单检查;
 * 3.  设置request的编码方式为UTF-8;
 * 4.  透传对index.jsp的访问，该访问会被重定向到/g?id=index;
 * 5.  限制只能访问/g;
 * 6.  检查url中是否带有id参数;
 * 7.  根据sid在session server中读出userBean，设置其back url，并置入request;
 * 8.  将sid置入request属性;
 * 9.  记录统计日志;
 * 10. 继续其他Filter;
 * 11. 从request中得到userBean，并修改其最后访问时间，回写入session server.
 * 
 */
public final class CommonFilter implements Filter
{	
	
    static {
        // God初始化
        try {
            God.getInstance().startWorld();
        }
        catch( Exception e ) {
            MyLogger.getNormal().fatal("start world fail on CommonFilter!", e);
        }
    }
    
    private static boolean worldIsStarted = true;
    
    public static boolean worldIsStarted() {
    	return worldIsStarted;
    }
    
    public static void stopPlay() {
    	worldIsStarted = false;
    }
    
	public void init(FilterConfig arg0) throws ServletException {
	}	

	public void destroy() {
	}
	
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException
	{
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
		
		if ( !worldIsStarted() ) {
			MyLogger.getNormal().info( "Stop play!" );
            Dispatcher.toStopPlayPage(req, res);
            return;
		}
        
        //打访问日志,为了精确地统计到每日的情况
        String queryString = req.getQueryString(); //访问的queryString
        String methodString = req.getMethod(); //方法,一般为POST或者GET
        String URI = req.getRequestURI(); //要访问的URI
        
        String profileMsg = "CommonFilter.doFilter, uri[" + URI + "]queryString[" + 
            queryString + "]method["+methodString+"]";
        UtilTimerStack.push(profileMsg);
        try {
    		String rip = Util.getRemoteIP(request); //获取远程的IP
    
    		//判断ip白名单
    		if (!IpTable.isAllowIp(rip))
    		{
    			MyLogger.getNormal().info("deny ip: " + rip + " " + req.getHeader("x-up-calling-line-id"));
                Dispatcher.toUserErrorPage(req, res, "ip受限！");
    			return;
    		}
    
    		// 判断ip黑名单
    		if (IpTable.isBlackIp(rip))
    		{
    			MyLogger.getNormal().info("deny black ip: " + rip + " " + req.getHeader("x-up-calling-line-id"));
    			Dispatcher.toUserErrorPage(req, res, "ip受限！");
    			return;
    		}
    
    		req.setCharacterEncoding("utf-8"); //强制设为utf-8编码
    		
            // 透传对index.jsp的访问，该访问会被重定向到/g?id=index
            if ( URI.equals("/") || URI.equals("/index.jsp") ) {
                chain.doFilter(request, response);
                return;
            }
            
            // 限制只能访问g
            if ( !URI.equals("/g") ) {
                MyLogger.getNormal().info( "URI[" + URI + "] is denied!" );
                Dispatcher.toUserErrorPage(req, res, "请求的页面地址不存在！");
                return;                
            }
            
            // 检查是否带有id参数
            String id = req.getParameter(Dispatcher.PARAM_PAGE_ID);
            if ( id == null )
            {
                MyLogger.getNormal().info("url中没有带参数" + Dispatcher.PARAM_PAGE_ID);
                Dispatcher.toUserErrorPage(req, res, "URL参数不合法");
                return;
            }
            
            int uid = 0;
            int crtTime = 0;
            int sessionKey = 0;
    		try
    		{
    			// 判断是否有带session id
    			String sid = req.getParameter("s");
    			if ( sid == null )
    			{
    				sid = "00";
    			}
    			sid = sid.replace(' ', '+'); //将空格转成+号，因为在URL中传+号的时候，getParameter会认出是空格
    
                // 将sid置入request
                request.setAttribute( "sid", sid );
    			
                // 生成userBean对象置入request
    			SessionUserBean user = null;
    			if ( !sid.equals("00") )
    			{
                    int[] decodedSid = SessionCoding.decode(sid);
                    if ( decodedSid != null )
                    {
                        uid = decodedSid[0];
                        crtTime = decodedSid[1];
                        sessionKey = decodedSid[2]; 
    
                        // 判读sid是否过期
                        long loginTimeDiff = (System.currentTimeMillis() / 1000) - crtTime;
                        if (loginTimeDiff > 24 * 3600 * 30)
                        {
                            String url = SystemConf.getInstance().getSidTimeoutUrl();
                            String serveraddr = request.getServerName();
                            if(request.getServerPort() != 80)
                                serveraddr += ":" + request.getServerPort();
                            String[] qs = StringUtil.split(queryString, "&");
                            ArrayList<String> ls = new ArrayList<String>();
                            for(int i = 0; i < qs.length; ++i){
                                if(!qs[i].startsWith("sid=") && !qs[i].startsWith("rand="))
                                    ls.add(qs[i]);
                            }
                            String q = StringUtil.join("&", ls);
                            url += "&go_url="+ URLEncoder.encode("http://" + serveraddr + URI + "?" + q, "utf-8");
                            MyLogger.getNormal().warn("loginTime is too old:" + sid + ", sendRedirect to " + url);
                            res.sendRedirect(url);
                            return;
                        } 
                        
                        try {
                        	user = SessionClient.getInstance().getUser(sid);
                        }
                        catch( Exception e )
                        {
                            ErrorPageBean errorpage = new ErrorPageBean("内部错误");
                            MyLogger.getNormal().error("[" + errorpage.getErrno() + "]get session user by sid[" + sid + "] fail ", e );
                            Dispatcher.toErrorPage(req, res, errorpage);
                            return;
                        }     
                        
                        if ( user == null ) {
                            MyLogger.getNormal().info( "uid[" + uid + "] login again, sid[" + sid + "] has invalidated!" );
                            String errorMsg = "您的登录信息已失效，请重新登录！";
                            Dispatcher.toErrorPage(req, res, errorMsg);
                            return;                            
                        }
                        
                        // 校验取到的userBean是否合法
                        if ( uid != user.getUserID() )
                        {
                            MyLogger.getNormal().error("sid 's uid[" + uid + "] can't match session server's uid[" + user.getUserID() + "]" );
                            Dispatcher.toErrorPage(req, res, "uid不合法");
                            return;
                        }
                        else if ( sessionKey != user.getSessionKey() )
                        {
                            MyLogger.getNormal().error("sid 's session key[" + sessionKey + "] can't match session server's session key[" + user.getSessionKey() + "]" );
                            Dispatcher.toErrorPage(req, res, "session key不合法");
                            return;
                        }
                        // 记录userBean
                        MyLogger.getNormal().debug( "set userBean: " + user.toString() );
                        request.setAttribute( "userBean", user );
                    }
                    else 
                    {
                        ErrorPageBean errorpage = new ErrorPageBean("内部错误");
                        MyLogger.getNormal().error("[" + errorpage.getErrno() + "]sid[" + sid + " decode fail!");
                        Dispatcher.toErrorPage(req, res, errorpage);
                        return;
                    }
    			}

                // 记录统计日志
                StringBuffer strBuf = new StringBuffer();
                strBuf.append(uid).append("|").append(rip).append("|").
                        append(id).append("|").append(methodString).append("|").
                        append(URI).append("?").append(queryString);
                MyLogger.getAccess().info(strBuf.toString());
                
                UtilTimerStack.push("chain.doFilter");
    			chain.doFilter(request, response);			
                UtilTimerStack.pop("chain.doFilter");

                if ( user != null )
                {
                	// 已登录用户
                    // 设置来源url
                    String aid = Dispatcher.getForwardId(req, res);
            		if ( !aid.equals("backAction") ) {
                        Map<String,Object> reqAttributes = new HashMap<String, Object>();
                        Enumeration<?> names = req.getAttributeNames();
                        while ( names.hasMoreElements() ) {
                        	String name = (String) names.nextElement();
                        	reqAttributes.put( name, req.getAttribute(name) );
                        }
                        String currURL = "";
                        if ( !aid.endsWith("Action") ) {
                        	// view页面
                        	currURL = URI
                                + "?"
                                + queryString.replaceAll("(rand=[0-9]+)?&(?!amp;)",
                                        "&");
        	                if (queryString.indexOf("rand=") < 0)
        	                {
        	                	currURL += "&rand=" + new Random().nextInt(999999);
        	                }
                        }
                        else {
                        	// controller页面
                        	String finalId = Dispatcher.getFinalId( req );
                        	currURL = "/g?s=" + sid
                                + "&id=" + finalId
                                + "&rand=" + new Random().nextInt(999999);
                        }
                        user.recordUrl( currURL, reqAttributes );
            		}
                    
                    // 处理返回页面的情况
                    if ( "scene".equals(aid) ) {
                    	// 主页面，清空返回url栈
                    	user.cleanBackUrlStack();
                    }
                	else {
                		if ( aid.equals("backAction") ) {
                			user.pollBackUrl();
                		}
                		else {
                			user.recordBackUrl();
                		}
                	}
                	
                	user.setAccessTime(System.currentTimeMillis());
                    SessionClient.getInstance().setUser(sid, user);
                }
            }
    		catch (Exception e)
    		{
    		    ErrorPageBean errorpage = new ErrorPageBean("内部错误");
    			MyLogger.getNormal().error("[" + errorpage.getErrno() + "]CommonFilter process error:queryString="
    					+ req.getQueryString(), e);
                Dispatcher.toErrorPage(req, res, errorpage);
    		}
        }
        finally {
          UtilTimerStack.pop(profileMsg); //this needs to be the same text as above
        }
	}
	
}
